const crypto = require('crypto');
const url = require('url');
const cheerio = require('cheerio');
const rp = require('request-promise');
const Client = require('aliyun-api-gateway').Client;
const client = new Client('24725606', 'b918d557ca663e468ec4f731c1ff6cc1');
const query = require('./utils/mysql');
const striptags = require('striptags');
const requestImageSize = require('request-image-size');
const source = require('./config').source;

let public_name = '';
let public_num = '';

let cookieJar = rp.jar();
const init = async () => {
  console.log('开始爬取公众号： ', public_name);
  console.log('........................');
  console.log('........................');
  // 公众号搜索
  const uri = `http://weixin.sogou.com/weixin?type=1&s_from=input&query=${public_num}&ie=utf8&_sug_=n&_sug_type_=`;
  const Referer = `http://weixin.sogou.com/antispider/?from=${uri.split('?')[1]}`;
  const headers = {
    'X-Requested-With': 'XMLHttpRequest',
    'Cookie': cookieJar.getCookieString(uri)
  };
  console.log('初始获取cookie');
  console.log(cookieJar.getCookieString(uri));
  console.log('........................');
  console.log('........................');
  headers.Referer = Referer;

  let $ = await rp({
    uri,
    headers: headers,
    transform: function (body) {
      return cheerio.load(body);
    },
    jar: cookieJar,
  }).catch(ex => console.log(ex));

  // 需要输入验证码
  if ($('.p3') && $('.p3').text().indexOf('验证码') !== -1) {
    const codeUrl = `http://weixin.sogou.com/antispider/${$('#seccodeImage').attr('src')}`;
    console.log('需要输入验证码');
    console.log(codeUrl);
    console.log('........................');
    console.log('........................');
    await verifyCode1(uri, codeUrl).catch(ex => {
      console.log(ex);
    });
    console.log('验证成功，重新访问...');
    console.log('........................');
    console.log('........................');
    await init().catch(ex => console.log(ex));
    return false;
  }

  const $li = $('.news-list2 li')[0];
  const ImgBox = $($li).find('.img-box');
  const url = ImgBox.find('a').attr('href');

  console.log('获取到地址公众号主页地址: ', url);
  console.log('........................');
  console.log('........................');

  await getList(url).catch(ex => console.log(ex));

};

const verifyCode1 = async (uri, imgUrl) => {
  const Referer = `http://weixin.sogou.com/antispider/?from=${uri.split('?')[1]}`;
  const verifycodeBase64 = await rp({
    uri: imgUrl,
    encoding: 'base64',
    headers: {
      'Cookie': cookieJar.getCookieString(Referer),
      'Referer': Referer
    },
    jar: cookieJar,
  }).catch(ex => console.log('验证码获取base64失败： ', ex));

  // const verifycode = `data:image/jpeg;base64,${code}`;
  // console.log('[verifycode base64]: ', verifycode);

  // 使用阿里验证码服务获取验证码文本
  const verifyCodeText = await client.post('http://jisuyzmsb.market.alicloudapi.com/captcha/recognize', {
    query: {
      type: 'en6',
    },
    data: {
      pic: verifycodeBase64,
    },
    headers: {
      'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8',
    },
  }).catch(ex => console.log(ex));

  if (Number.parseInt(verifyCodeText.status, 10) === 0) {
    console.log('[识别出的验证码文本：]', verifyCodeText.result.code);
    // 提交验证码
    const goVerify = await rp({
      method: 'POST',
      uri: 'http://weixin.sogou.com/antispider/thank.php',
      form: {
        c: verifyCodeText.result.code,
        r: url.parse(uri).path,
        v: 5,
      },
      headers: {
        'Referer': Referer,
        'Cookie': cookieJar.getCookieString(imgUrl)
      },
      jar: cookieJar,
      json: true,
      transform: function (body) {
        cookieJar.setCookie(
          rp.cookie('seccodeRight=success'),
          uri
        );
        cookieJar.setCookie(
          rp.cookie(`SUV=${1E3 * (new Date).getTime() + Math.round(1E3 * Math.random())}`),
          uri
        );
        cookieJar.setCookie(
          rp.cookie(`SNUID=${body.id}`),
          uri
        );
        return body
      }
    }).catch(ex => console.log(ex));
    // 验证成功
    if (goVerify.code === 0) {
      console.log('验证成功');
      return true;
    }

    console.log('验证失败，重试');
    await verifyCode1(uri, imgUrl).catch(ex => console.log(ex));

  }
};

/**
 * 过滤图片
 * @param images
 * @returns {Promise.<{good: Array, bad: Array}>}
 */
const imagesFilter = async (images) => {
  let good = [];
  const bad = [];
  if (!Array.isArray(images) || images.length === 0) {
    return { good, bad };
  }
  for (let j = 0; j < images.length; j++) {
    const cur = images[j];
    const size = await requestImageSize(cur).catch(ex => console.log(ex));
    const { width, height, type } = size;
    if (width > 300 && height > 100 && (type === 'jpg' || type === 'jpeg' || type === 'png')) {
      good.push(cur);
    } else {
      bad.push(cur);
    }
  }
  return { good, bad };
};

// 获取公众号文章列表
const getList = async (url) => {
  const Referer = `http://weixin.sogou.com/antispider/?from=${url.split('?')[1]}`;
  const headers = {
    'X-Requested-With': 'XMLHttpRequest',
    'Cookie': cookieJar.getCookieString(url),
    'Referer': Referer,
  };
  $ = await rp({
    uri: url,
    headers,
    transform: function (html) {
      return cheerio.load(html);
    },
    jar: cookieJar,
  }).catch(ex => console.log(ex));

  const $verifycode = $('.verifycode');
  if ($verifycode && $verifycode.text().indexOf('验证码') != -1) {
    const codeUrl = `http://mp.weixin.qq.com/mp/verifycode?cert=${(new Date).getTime() + Math.random()}`;
    console.log('需要输入验证码: ', codeUrl);
    console.log('........................');
    console.log('........................');
    await verifyCode2(url, codeUrl).catch(ex => console.log(ex));
    console.log('验证成功，重新访问...');
    console.log('........................');
    console.log('........................');
    await getList(url).catch(ex => console.log(ex));
    return false;
  }

  const html = $('body').html();
  let msglist = html.match(/var msgList = ({.+}}]});?/);
  if (!msglist) {
    console.log(`-没有搜索到 ${public_num} 的文章,只支持订阅号,服务号不支持!`);
    process.exit(0);
  }
  msglist = msglist[1];
  msglist = msglist.replace(/(&quot;)/g, '\\\"').replace(/(&nbsp;)/g, '');
  msglist = JSON.parse(msglist);
  console.log('获取文章列表成功');
  console.log('........................');
  console.log('........................');
  const list = msglist.list;
  const arr = [];
  for (let i = 0, len = list.length; i < len; i++) {
    const { app_msg_ext_info, comm_msg_info } = list[i];
    const { content_url, cover, digest, title, multi_app_msg_item_list } = app_msg_ext_info;
    const { id: aid, datetime } = comm_msg_info;
    arr.push({
      aid,
      datetime,
      content_url: `http://mp.weixin.qq.com${content_url.replace(/&amp;/img, '&')}`,
      cover,
      digest,
      title,
      public_name,
      public_num,
    });
    if (Array.isArray(multi_app_msg_item_list) && multi_app_msg_item_list.length > 0) {
      for (let j = 0, leng = multi_app_msg_item_list.length; j < leng; j++) {
        const cur = multi_app_msg_item_list[j];
        const { content_url, cover, digest, title } = cur;
        arr.push({
          aid,
          datetime,
          content_url: `http://mp.weixin.qq.com${content_url.replace(/&amp;/img, '&')}`,
          cover,
          digest,
          title,
          public_name,
          public_num,
        });
      }
    }
  }
  console.log('开始获取文章');
  console.log('........................');
  console.log('........................');

  const getArticle = async (uri) => {
    const headers = {
      'X-Requested-With': 'XMLHttpRequest',
      'Cookie': cookieJar.getCookieString(url),
      'Referer': url,
      'Host': 'mp.weixin.qq.com',
    };
    const $ = await rp({
      uri,
      headers,
      transform: function (body) {
        return cheerio.load(body);
      },
    }).catch(ex => console.log(ex));
    const html = $('#js_content').html();
    return html;
  };

  for (let i = 0, len = arr.length; i < len; i++) {
    const html = await getArticle(arr[i].content_url).catch(ex => {
        console.log('获取文章内容出错');
        console.log('........................');
        console.log('........................');
        arr[i].images = [];
        arr[i].content = '';
      }) || '';
    let imagesVideos = html.match(/data-src=".*?"/img) || [];

    imagesVideos = imagesVideos.map(item => item.slice(item.indexOf('="') + 2, -1));
    const images = imagesVideos.filter(item => /^http(s)?:\/\/mmbiz/img.test(item));
    // TODO 视频
    const videos = imagesVideos.filter(item => /^http(s)?:\/\/v\.qq/img.test(item));

    const imgagesFilter = await imagesFilter(images).catch(ex => {
      console.log('过滤图片出错');
    });
    arr[i].images = imgagesFilter.good;
    arr[i].videos = videos;

    // 去除文章中的辣鸡图片，并给好的视频和图片加占位符
    let text = html.replace(/data-src="(.*?)"/img, (str, $1) => {
      if (videos.includes($1)) {
        return `/>   [[${$1}]]   <img `;
      } else if (imgagesFilter.good.includes($1)) {
        return `/>   {{${$1}}}   <img `;
      } else {
        return '';
      }
    }).replace(/\n\s+/img, '\n');

    arr[i].content = striptags(text);
  }

  console.log('获取文章列表成功');
  console.log('文章总数： ', arr.length);
  console.log('........................');
  console.log('........................');
  // console.log(arr);

  const foo = (text) => {
    return text.replace(/\s\n/img, '').replace(/\s{3,}/img, '').replace(/['"](.*?)['"]/img, '“$1”');
  };

  // TODO video 保存及前端展示

  const values = arr.filter(item => item.content && item.videos.length === 0).reduce((acc, curr, index, arr) => {
    const { aid, datetime, content, images, cover, digest, title, public_name, public_num } = curr;
    return `${acc}("${aid}", "${datetime}", "${content}", "${images}", "${cover}", "${foo(digest)}", "${foo(title)}", "${public_name}", "${public_num}")${index < arr.length - 1 ?
      ',' :
      ''}`;
  }, '');

  console.log('上传数据库');
  console.log('........................');
  console.log('........................');

  // 存入数据库
  const res = await query(`INSERT INTO articles (aid, datetime, content, images, cover, digest, title, public_name, public_num) VALUES ${values}`);

  if (res instanceof Error || res.affectedRows === 0) {
    console.log('[error]');
    console.log(res);
    console.log('........................');
    console.log('........................');
    return;
  }

  console.log('上传成功');
  console.log('........................');
  console.log('........................');
};

const verifyCode2 = async (uri, imgUrl) => {
  const cert = imgUrl.split('=')[1];
  const Referer = `http://weixin.sogou.com/antispider/?from=${uri.split('?')[1]}`;
  const verifycodeBase64 = await rp({
    uri: imgUrl,
    encoding: 'base64',
    headers: {
      'Cookie': cookieJar.getCookieString(Referer),
      'Referer': Referer
    },
    jar: cookieJar,
  }).catch(ex => console.log('验证码获取base64失败： ', ex));

  // const verifycode = `data:image/jpeg;base64,${code}`;
  // console.log('[verifycode base64]: ', verifycode);

  // 使用阿里验证码服务获取验证码文本
  const verifyCodeText = await client.post('http://jisuyzmsb.market.alicloudapi.com/captcha/recognize', {
    query: {
      type: 'e4',
    },
    data: {
      pic: verifycodeBase64,
    },
    headers: {
      'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8',
    },
  }).catch(ex => console.log(ex));

  if (Number.parseInt(verifyCodeText.status, 10) === 0) {
    console.log('识别出的验证码文本');
    console.log(verifyCodeText.result.code);
    console.log('........................');
    console.log('........................');

    const verifycode = verifyCodeText.result.code;
    // 提交验证码
    const goVerify = await rp({
      method: 'POST',
      uri: `http://mp.weixin.qq.com/mp/verifycode`,
      form: {
        input: encodeURIComponent(verifycode),
        cert: encodeURIComponent(cert)
      },
      headers: {
        'Referer': Referer,
        'Cookie': cookieJar.getCookieString(imgUrl)
      },
      jar: cookieJar,
      json: true,
      transform: function (body) {
        return body
      }
    }).catch(ex => console.log(ex));
    // 验证成功
    if (goVerify.code === 0) {
      console.log('验证成功');
      return true;
    }

    console.log('验证失败，重试');
    await verifyCode1(uri, imgUrl).catch(ex => {
      console.log(ex);
    });

  }
};

// init();

(async () => {
  for (let i = 0, len = source.length; i < len; i++) {
    const cur = source[i];
    public_num = cur.public_num;
    public_name = cur.public_name;
    await init();
    console.log('****************************************************');
    console.log('****************************************************');
    console.log('****************************************************');
    console.log('****************************************************');
    console.log('****************************************************');
    console.log('****************************************************');
    console.log('****************************************************');
    console.log('****************************************************');
  }
})();
